﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Threading;
using System.Configuration;
using CloudBackupLib;

namespace CloudBackupCmd
{
    class Program
    {
        enum BackupStep
        {
            Export,
            Compress,
            Upload,
            Done
        };

        enum RestoreStep
        {
            Download,
            Uncompress,
            Import,
            Done
        };

        static string _ExportLocation = ConfigurationManager.AppSettings["ExportLocation"];
        static string _CompressLocation = ConfigurationManager.AppSettings["CompressLocation"];
        static string _DownloadLocation = ConfigurationManager.AppSettings["DownloadLocation"];
        static string _UncompressLocation = ConfigurationManager.AppSettings["UncompressLocation"];

        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("CloudBackup v1.0 Command line utility");


                if(args.Length == 0)
                {
                    throw new ArgumentException();
                }
                else
                {
                    switch(args[0].ToLower())
                    {
                        case "backup":
                        {
                            if(args.Length != 2) throw new ArgumentException();

                            VirtualMachine virtualMachine = VirtualMachine.GetLocalVirtualMachines().Where(vm => vm.Name == args[1]).FirstOrDefault();
                            if(virtualMachine == null) throw new ArgumentException();

                            Console.WriteLine("Command: backup, Name: " + virtualMachine.Name);
                            Backup(virtualMachine);

                            break;
                        }
                        case "restore":
                        {
                            if(args.Length != 2) throw new ArgumentException();

                            VirtualMachine virtualMachine = VirtualMachine.GetRemoteVirtualMachines().Where(vm => vm.Name == args[1]).FirstOrDefault();
                            if(virtualMachine == null) throw new ArgumentException();

                            Console.WriteLine("Command: restore, Name: " + virtualMachine.Name);
                            Restore(virtualMachine);

                            break;
                        }
                        case "listlocal":
                        {
                            Console.WriteLine("Command: listlocal");

                            foreach(VirtualMachine vm in VirtualMachine.GetLocalVirtualMachines().OrderBy(vm => vm.Name))
                            {
                                Console.WriteLine("  Name: " + vm.Name);
                            }
                            break;
                        }
                        case "listremote":
                        {
                            Console.WriteLine("Command: listremote");

                            foreach(VirtualMachine vm in VirtualMachine.GetRemoteVirtualMachines().OrderBy(vm => vm.Name))
                            {
                                Console.WriteLine("  Name: " + vm.Name);
                            }
                            break;
                        }
                        case "clearremote":
                        {
                            Console.WriteLine("Command: clearremote");

                            foreach(VirtualMachine vm in VirtualMachine.GetRemoteVirtualMachines().OrderBy(vm => vm.Name))
                            {
                                Console.WriteLine("  Deleting remote VM: " + vm.Name);
                                vm.Delete(null);
                            }

                            break;
                        }
                        default:
                        {
                            throw new ArgumentException();
                        }
                    }

                    Console.WriteLine("Done.");
                }
            }
            catch(ArgumentException)
            {
                DisplayHelp();
            }
            catch(ApplicationException e)
            {
                Console.WriteLine("Error: " + e.Message);
            }

            return;
        }

        static void Backup(VirtualMachine virtualMachine)
        {
            BackupStep step = BackupStep.Export;
            bool stepCompleted = false;

            bool asyncResult = true;

            // Async callback for the step methods
            //
            Action<VirtualMachine, bool> callback = delegate(VirtualMachine context, bool success)
            {
                asyncResult = success;
                stepCompleted = true;
            };

            while(step != BackupStep.Done)
            {
                stepCompleted = false;

                // Execute the next step in the restore process asynchronously
                //
                switch(step)
                {
                    case BackupStep.Export:
                    {
                        Console.Write("  Exporting: ");
                        virtualMachine.Export(_ExportLocation, callback);
                        break;
                    }
                    case BackupStep.Compress:
                    {
                        Console.Write("  Compressing: ");
                        virtualMachine.Compress(Path.Combine(_ExportLocation, virtualMachine.Name), _CompressLocation, callback);
                        break;
                    }
                    case BackupStep.Upload:
                    {
                        Console.Write("  Uploading: ");
                        virtualMachine.Upload(Path.Combine(_CompressLocation, virtualMachine.Name) + ".zip", callback);
                        break;
                    }
                }

                // Poll for completion
                //
                int cursorLeft = Console.CursorLeft;
                Console.Write("0%");

                while(stepCompleted == false)
                {
                    Console.CursorLeft = cursorLeft;
                    int? percentComplete = virtualMachine.GetPercentComplete();
                    if(percentComplete != null)
                    {
                        Console.Write(percentComplete + "%");
                    }

                    Thread.Sleep(200);
                }

                // Check the result of the step
                //
                if(asyncResult)
                {
                    Console.CursorLeft = cursorLeft;
                    Console.WriteLine("100%");

                    ++step; // Proceed to the next step if successful
                }
                else // Otherwise handle errors
                {
                    Console.WriteLine();

                    switch(step)
                    {
                        case BackupStep.Export:
                        {
                            throw new ApplicationException("Export failed");
                        }
                        case BackupStep.Compress:
                        {
                            throw new ApplicationException("Compress failed");
                        }
                        case BackupStep.Upload:
                        {
                            throw new ApplicationException("Upload failed");
                        }
                    }
                }
            }

            Console.WriteLine("Operation Successful.");
        }

        static void Restore(VirtualMachine virtualMachine)
        {
            RestoreStep step = RestoreStep.Download;
            bool stepCompleted = false;

            bool asyncResult = true;

            // Async callback for the step methods
            //
            Action<VirtualMachine, bool> callback = delegate(VirtualMachine context, bool success)
            {
                asyncResult = success;
                stepCompleted = true;
            };

            while(step != RestoreStep.Done)
            {
                stepCompleted = false;

                // Execute the next step in the restore process asynchronously
                //
                switch(step)
                {
                    case RestoreStep.Download:
                    {
                        Console.Write("  Downloading: ");
                        virtualMachine.Download(_DownloadLocation, callback);
                        break;
                    }
                    case RestoreStep.Uncompress:
                    {
                        Console.Write("  Uncompressing: ");
                        virtualMachine.Uncompress(Path.Combine(_DownloadLocation, virtualMachine.Name) + ".zip", _UncompressLocation, callback);
                        break;
                    }
                    case RestoreStep.Import:
                    {
                        Console.Write("  Importing: ");
                        virtualMachine.Import(Path.Combine(_UncompressLocation, virtualMachine.Name), callback);
                        break;
                    }
                }

                // Poll for completion
                //
                int cursorLeft = Console.CursorLeft;
                Console.Write("0%");

                while(stepCompleted == false)
                {
                    Console.CursorLeft = cursorLeft;
                    int? percentComplete = virtualMachine.GetPercentComplete();
                    if(percentComplete != null)
                    {
                        Console.Write(percentComplete + "%");
                    }

                    Thread.Sleep(200);
                }

                // Check the result of the step
                //
                if(asyncResult)
                {
                    Console.CursorLeft = cursorLeft;
                    Console.WriteLine("100%");

                    ++step; // Proceed to the next step if successful
                }
                else // Otherwise handle errors
                {
                    Console.WriteLine();

                    switch(step)
                    {
                        case RestoreStep.Download:
                        {
                            throw new ApplicationException("Download failed");
                        }
                        case RestoreStep.Uncompress:
                        {
                            throw new ApplicationException("Uncompress failed");
                        }
                        case RestoreStep.Import:
                        {
                            throw new ApplicationException("Import failed");
                        }
                    }
                }
            }

            Console.WriteLine("Operation Successful.");
        }

        static void DisplayHelp()
        {
            Console.WriteLine("Usage:");
            Console.WriteLine("  CloudBackupCmd.exe <cmd> <name>");
            Console.WriteLine("    cmd : Can be one of the following values");
            Console.WriteLine("      backup - Backup the specified virtual machine");
            Console.WriteLine("      restore - Restore the specified virtual machine");
            Console.WriteLine("      listlocal - List local virtual machines");
            Console.WriteLine("      listremote - List remote virtual machines");
            Console.WriteLine("      clearremote - Remove all remote virtual machines");
            Console.WriteLine("    name : Specifies the name of the virtual machine to operate on");
            Console.WriteLine("  Use the CloudBackupCmd.exe.config file to specify options");
        }
    }
}
